metadata = read_tsv(here('data', 'metadata_merged.tsv'), col_types = cols()) %>%
condition = str_c(CornVariety, FungalStrain, TissueExtraction, sep='_')) %>%
rename(Corn_Genotype = CornVariety, Fungal_Treatment = FungalStrain, Tissue_Extraction = TissueExtraction)
get_counts = function(.) otu_table(.) %>% as.data.frame() %>% rownames_to_column('feature_id') %>% as_tibble()
get_tax = function(.) tax_table(.) %>% data.frame() %>% rownames_to_column('feature_id') %>% as_tibble()
get_qza = function(filepath){read_qza(filepath)$data %>% as.data.frame() %>% rownames_to_column('SampleID')}
mutate(SampleID = str_replace(SampleID, '(.*?)-(.*)', '\\2-\\1'),
SampleID = str_replace_all(SampleID, c('-B73'='-373', '-C322'='-322')))
change_16S_ids_to_match_ITS = . %>%
mutate(SampleID = str_replace(SampleID, '(.*?)-(.*)', '\\2-\\1'),
ps = list('16S'= qza_to_phyloseq(features = here('data', '16S', 'table_wo_outliers.qza'),
tree=here('data', '16S', 'rooted-tree.qza'),
taxonomy=here('data', '16S', 'merged-taxonomy.qza'),
metadata=here('data', '16S', 'metadata_filtered.tsv'),
tmp='C:/Users/brian.mack/Downloads/tmp') %>%
subset_samples(CornVariety != 'dummy' & Timepoint == '2' & TissueType == 'Ovule') %>%
prune_taxa(taxa_sums(.) > 0, .),
'ITS' = qza_to_phyloseq(features = here('data', 'ITS', 'table-no-mitochondria-no-chloroplast.qza'),
tree=here('data', 'ITS', 'rooted-tree.qza'),
taxonomy=here('data', 'ITS', 'merged-taxonomy.qza'),
metadata=here('data', 'ITS', 'metadata_merged.tsv'),
tmp='C:/Users/brian.mack/Downloads/tmp') %>%
subset_samples(CornVariety != 'dummy' & Timepoint == '2' & TissueType == 'Ovule') %>%
prune_taxa(taxa_sums(.) > 0, .)
)
ps = map(ps, function(.x){
sample_data(.x) = get_sample_data(.x) %>%
rename(TimePoint = Timepoint) %>%
mutate(TimePoint = str_replace(TimePoint, '^', 'T'),
condition = str_c(CornVariety, FungalStrain, TissueExtraction, sep='_')) %>%
rename(Corn_Genotype = CornVariety, Fungal_Treatment = FungalStrain, Tissue_Extraction = TissueExtraction) %>%
group_by(condition) %>%
mutate(biological_rep = row_number()) %>%
ungroup() %>%
mutate(condition_w_rep = str_c(condition, biological_rep, sep='_')) %>%
column_to_rownames('SampleID')
return(.x)})
sample_data(ps$ITS) = get_sample_data(ps$ITS) %>%
change_its_ids_to_match_16S() %>%
column_to_rownames('SampleID')
ps %>% imap(~get_counts(.x) %>%
left_join(get_tax(.x), by='feature_id') %>%
rename(all_of(get_sample_data(.x) %>% pull(SampleID, name=condition_w_rep))) %>%
write_csv(here(str_glue('output/raw_counts_and_taxonomy_{.y}.csv')))
)
Removed taxa not assigned to a phylum. After removing these taxa, 1
16S samples was removed due to having total counts <= 1500.
ps_phylum_filt = list('16S' = subset_taxa(ps$`16S`, !is.na(Phylum) &
prune_samples(sample_sums(.) >= 1500, .) %>% prune_taxa(taxa_sums(.) > 0, .),
'ITS' = subset_taxa(ps$ITS, !is.na(Phylum) &
!Phylum %in% c("", 'uncharacterized', 'unidentified')) %>%
prune_samples(sample_sums(.) >= 1500, .) %>% prune_taxa(taxa_sums(.) > 0, .))
ps_phylum_filt_tax = ps_phylum_filt %>% map(get_tax)
ps_phylum_filt_counts = ps_phylum_filt %>% map(get_counts)
ps_phylum_filt_ra = map(ps_phylum_filt, ~transform_sample_counts(.x, function(x){x / sum(x)}))
ps_phylum_filt
phyloseq-class experiment-level object
otu_table() OTU Table: [ 946 taxa and 48 samples ]:
sample_data() Sample Data: [ 48 samples by 10 sample variables ]:
tax_table() Taxonomy Table: [ 946 taxa by 7 taxonomic ranks ]:
phy_tree() Phylogenetic Tree: [ 946 tips and 919 internal nodes ]:
taxa are rows
sample_data() Sample Data: [ 50 samples by 9 sample variables ]:tax_table() Taxonomy Table: [ 246 taxa by 7 taxonomic ranks ]:
ps_phylum_filt %>% imap(function(ps_phylum_filt, seq_type){
ps_phylum_filt %>% get_tax() %>%
summarize(across(-feature_id, ~sum(!is.na(.x))/ n())) %>%
round(2) %>%
pivot_longer(everything(), names_to = 'Taxonomic rank', values_to = seq_type)
}) %>%
reduce(full_join, by='Taxonomic rank') %>%
kbl(format = 'html', caption='Fraction of ASVs classified at each rank') %>%
kable_classic(full_width = F, html_font = "Times New Roman")
Fraction of ASVs classified at each rank
| Taxonomic rank |
16S |
ITS |
| Kingdom |
1.00 |
1.00 |
| Phylum |
1.00 |
1.00 |
| Class |
1.00 |
0.99 |
| Order |
1.00 |
0.93 |
| Family |
0.99 |
0.89 |
| Genus |
0.91 |
0.86 |
| Species |
0.28 |
0.73 |
ps_phylum_filt %>% imap(function(ps_phylum_filt, seq_type){
ps_phylum_filt %>% sample_sums() %>% enframe(name = 'SampleID', value = 'total_counts') %>%
left_join(metadata, by='SampleID') %>%
mutate(seq_type = seq_type)
}) %>%
bind_rows() %>%
ggplot(aes(x=condition, y=total_counts)) +
geom_boxplot(outlier.shape = NA) +
ggbeeswarm::geom_quasirandom(alpha = 0.3, width=0.2, groupOnX=TRUE) +
facet_wrap(~seq_type, ncol=1, scales = 'free') +
scale_y_continuous(labels = scales::comma) +
labs(title='Total number of counts for each sample') +
ggeasy::easy_rotate_x_labels() +
ggeasy::easy_center_title()

Rarefying to 1569 counts.
ps_phylum_filt_rarefied = ps_phylum_filt %>% map(
~rarefy_even_depth(.x, sample.size = 1569, rngseed=1100 , replace=FALSE) %>%
prune_taxa(taxa_sums(.) > 0, .))
ps_phylum_filt_rarefied
$`16S`
otu_table() OTU Table: [ 579 taxa and 48 samples ]:
sample_data() Sample Data: [ 48 samples by 10 sample variables ]:
phyloseq-class experiment-level object
sample_data() Sample Data: [ 50 samples by 9 sample variables ]:
phy_tree() Phylogenetic Tree: [ 111 tips and 109 internal nodes ]:taxa are rows
prev = map(ps_phylum_filt_ra,
function(ps_phylum_filt_ra){
relative_counts = ps_phylum_filt_ra %>% get_counts()
tax = ps_phylum_filt_ra %>% get_tax()
relative_counts %>%
reframe(feature_id = feature_id,
prevalence = rowSums(.> 0) -1, #subtracted 1 because feature_id column is always counted
total_relative_abundance = taxa_sums(ps_phylum_filt_ra),
Phylum = tax$Phylum,
Genus = tax$Genus,
prev_plots = prev %>% imap(function(prev, seq_type){
mutate(prevalence = prevalence / nsamples(ps_phylum_filt[[seq_type]])) %>%
ggplot(aes(total_relative_abundance, prevalence, color=Phylum, text=glue('Genus: {Genus}<br>Species: {Species}'))) +
geom_hline(yintercept = 0.05, alpha = 0.5, linetype = 2) +
scale_x_log10() + xlab("Total Relative Abundance") + ylab("Prevalence [Fraction Samples]") +
facet_wrap(~Phylum) +
theme(plot.title = element_text(hjust = 0.5)) +
theme(legend.position="none")})
prev_plots$`16S` %>% plotly::ggplotly()
prev_plots$ITS %>% plotly::ggplotly()
Predominant phyla
ps_phyla = map(ps_phylum_filt,
function(ps_phylum_filt){
ps_phyla = tax_glom(ps_phylum_filt, "Phylum", NArm = TRUE)
prevalenceThreshold = 0.05 * nsamples(ps_phyla)
get_counts() %>%
summarise(feature_id = feature_id,
prevalence = rowSums(.> 0),
prune_taxa(., ps_phyla) %>%
prune_samples(sample_sums(.) >= 500, .) %>%
prune_taxa(taxa_sums(.) > 0, .)
sample_data(ps_phyla) = sample_data(ps_phyla) %>%
as('data.frame') %>%
mutate(TissueType = fct_relevel(TissueType, 'Ovule'))
Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in dplyr 1.1.0.
Please use `reframe()` instead.
When switching from `summarise()` to `reframe()`, remember that `reframe()` always returns an ungrouped data frame and adjust accordingly.Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in dplyr 1.1.0.
Please use `reframe()` instead.
When switching from `summarise()` to `reframe()`, remember that `reframe()` always returns an ungrouped data frame and adjust accordingly.
ps_phyla_ra = map(ps_phyla, ~transform_sample_counts(.x, function(x){x / sum(x)}))
prev_corn_phyla = map(ps_phyla_ra,
relative_counts = ps_phyla_ra %>% get_counts()
tax = ps_phyla_ra %>% get_tax()
relative_counts %>%
pivot_longer(-feature_id, names_to = 'SampleID', values_to = 'counts') %>%
left_join(metadata, by='SampleID') %>%
left_join(tax, by='feature_id') %>%
group_by(Phylum) %>%
mutate(prevalence_entire_dataset = sum(counts > 0) / n(),
mean_relative_abundance_entire_dataset = mean(counts)) %>%
summarize(prevalence = sum(counts > 0) / n(),
prevalence_entire_dataset = first(prevalence_entire_dataset),
mean_relative_abundance_entire_dataset = first(mean_relative_abundance_entire_dataset)) %>%
ungroup()})
prev_corn_phyla$`16S` %>%
rename(total = mean_relative_abundance_entire_dataset) %>%
distinct(Phylum, Corn_Genotype, mean_relative_abundance, total) %>%
mutate(mean_relative_abundance= round(mean_relative_abundance, 3),
total = round(total, 3)) %>%
pivot_wider(names_from = Corn_Genotype, values_from = mean_relative_abundance) %>%
filter(total >= 0.01) %>%
'Relative abundance B73', 'Relative abundance CML322')) %>%
| Phylum |
Total Relative Abundance |
Relative abundance B73 |
Relative abundance CML322 |
| Proteobacteria |
0.866 |
0.829 |
0.899 |
| Firmicutes |
0.065 |
0.118 |
0.016 |
| Actinobacteriota |
0.038 |
0.041 |
0.035 |
| Bacteroidota |
0.030 |
0.010 |
0.050 |
prev_corn_phyla$ITS %>%
rename(total = mean_relative_abundance_entire_dataset) %>%
distinct(Phylum, Corn_Genotype, mean_relative_abundance, total) %>%
mutate(mean_relative_abundance= round(mean_relative_abundance, 3),
pivot_wider(names_from = Corn_Genotype, values_from = mean_relative_abundance) %>%
#filter(total >= 0.01) %>%
arrange(desc(total)) %>%
kbl(format = 'html', col.names = c('Phylum', 'Total Relative Abundance',
'Relative abundance B73', 'Relative abundance CML322')) %>%
| Phylum |
Total Relative Abundance |
Relative abundance B73 |
Relative abundance CML322 |
| Ascomycota |
0.999 |
0.999 |
0.999 |
| Basidiomycota |
0.001 |
0.001 |
0.001 |
Removed taxa that are present in less than 5% of samples
for ASV level dataset. This will be used for differential abundance
testing at ASV level.
ps_prevf = map2(ps_phylum_filt, prev,
function(ps_phylum_filt, prev){
keepTaxa = filter(prev, prevalence >= prevalenceThreshold) %>%
pull(feature_id)
prune_taxa(keepTaxa, ps_phylum_filt) %>%
prune_taxa(taxa_sums(.) > 0, .)})
ps_prevf_ra = map(ps_prevf, ~transform_sample_counts(.x, function(x){x / sum(x)}))
ps_prevf_clr = map(ps_prevf, microbiome::transform, 'clr')
ps_prevf_alr = map(ps_prevf, microbiome::transform, 'alr', shift=1)
ps_prevf
$`16S`
phyloseq-class experiment-level object
otu_table() OTU Table: [ 287 taxa and 48 samples ]:
sample_data() Sample Data: [ 48 samples by 10 sample variables ]:
tax_table() Taxonomy Table: [ 287 taxa by 7 taxonomic ranks ]:phy_tree() Phylogenetic Tree: [ 287 tips and 278 internal nodes ]:taxa are rows$ITS
otu_table() OTU Table: [ 51 taxa and 50 samples ]:sample_data() Sample Data: [ 50 samples by 9 sample variables ]:
tax_table() Taxonomy Table: [ 51 taxa by 7 taxonomic ranks ]:phy_tree() Phylogenetic Tree: [ 51 tips and 50 internal nodes ]:taxa are rows
Agglomerated counts at both genus level and species
level.
ps_genus = map(ps_phylum_filt,
function(ps_phylum_filt){
ps_genus = tax_glom(ps_phylum_filt, "Genus", NArm = TRUE)
ps_genus = ps_genus %>%
summarise(feature_id = feature_id,
prevalence = rowSums(.> 0),
TotalAbundance = taxa_sums(ps_genus)) %>%
filter(prevalence >= prevalenceThreshold) %>%
prune_taxa(., ps_genus) %>%
prune_samples(sample_sums(.) >= 500, .) %>%
prune_taxa(taxa_sums(.) > 0, .)
sample_data(ps_genus) = sample_data(ps_genus) %>%
as('data.frame') %>%
mutate(TissueType = fct_relevel(TissueType, 'Ovule'))
return(ps_genus)})
Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in dplyr 1.1.0.
Please use `reframe()` instead.
When switching from `summarise()` to `reframe()`, remember that `reframe()` always returns an ungrouped data frame and adjust accordingly.Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in dplyr 1.1.0.
Please use `reframe()` instead.
When switching from `summarise()` to `reframe()`, remember that `reframe()` always returns an ungrouped data frame and adjust accordingly.
ps_genus_ra = map(ps_genus, ~transform_sample_counts(.x, function(x){x / sum(x)}))
ps_genus_clr = map(ps_genus, microbiome::transform, 'clr')
ps_genus_alr = map(ps_genus, microbiome::transform, 'alr', shift=1)
ps_species = map(ps_phylum_filt,
function(ps_phylum_filt){
ps_species = tax_glom(ps_phylum_filt, "Species", NArm = TRUE)
prevalenceThreshold = 0.05 * nsamples(ps_species)
ps_species = ps_species %>%
get_counts() %>%
summarise(feature_id = feature_id,
prune_samples(sample_sums(.) >= 500, .) %>%
prune_taxa(taxa_sums(.) > 0, .)
sample_data(ps_species) = sample_data(ps_species) %>%
as('data.frame') %>%
mutate(TissueType = fct_relevel(TissueType, 'Ovule'))
return(ps_species)})
Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in dplyr 1.1.0.
Please use `reframe()` instead.
When switching from `summarise()` to `reframe()`, remember that `reframe()` always returns an ungrouped data frame and adjust accordingly.Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in dplyr 1.1.0.
Please use `reframe()` instead.
When switching from `summarise()` to `reframe()`, remember that `reframe()` always returns an ungrouped data frame and adjust accordingly.
ps_species_ra = map(ps_species, ~transform_sample_counts(.x, function(x){x / sum(x)}))
ps_species_clr = map(ps_species, microbiome::transform, 'clr')
ps_species_alr = map(ps_species, microbiome::transform, 'alr', shift=1)
Most prevalent genera
prev_corn_genus = map(ps_genus_ra,
function(ps_genus_ra){
relative_counts = ps_genus_ra %>% get_counts()
tax = ps_genus_ra %>% get_tax()
relative_counts %>%
pivot_longer(-feature_id, names_to = 'SampleID', values_to = 'counts') %>%
left_join(tax, by='feature_id') %>%
mean_relative_abundance_entire_dataset = mean(counts)) %>%
group_by(Genus, Corn_Genotype) %>%
summarize(prevalence = sum(counts > 0) / n(),
mean_relative_abundance = mean(counts),
prevalence_entire_dataset = first(prevalence_entire_dataset),
mean_relative_abundance_entire_dataset = first(mean_relative_abundance_entire_dataset)) %>%
ungroup()})
prev_corn_genus$`16S` %>%
rename(total = mean_relative_abundance_entire_dataset) %>%
distinct(Genus, Corn_Genotype, mean_relative_abundance, total) %>%
mutate(mean_relative_abundance= round(mean_relative_abundance, 3),
total = round(total, 3)) %>%
pivot_wider(names_from = Corn_Genotype, values_from = mean_relative_abundance) %>%
filter(total >= 0.01) %>%
arrange(desc(total)) %>%
kable_classic(full_width = F, html_font = "Times New Roman")
| Genus |
Total Relative Abundance |
Relative abundance B73 |
Relative abundance CML322 |
| Pantoea |
0.461 |
0.419 |
0.500 |
| Klebsiella |
0.094 |
0.024 |
0.159 |
| Enterobacter |
0.051 |
0.101 |
0.005 |
| Carnimonas |
0.041 |
0.085 |
0.000 |
| Burkholderia-Caballeronia-Paraburkholderia |
0.030 |
0.048 |
0.013 |
| Serratia |
0.029 |
0.033 |
0.026 |
| Stenotrophomonas |
0.029 |
0.014 |
0.043 |
| Lactococcus |
0.024 |
0.048 |
0.003 |
| Sphingobacterium |
0.024 |
0.007 |
0.039 |
| Achromobacter |
0.023 |
0.008 |
0.036 |
| Rosenbergiella |
0.022 |
0.047 |
0.000 |
| Listeria |
0.019 |
0.040 |
0.000 |
| Allorhizobium-Neorhizobium-Pararhizobium-Rhizobium |
0.017 |
0.009 |
0.023 |
| Ochrobactrum |
0.017 |
0.013 |
0.020 |
| Enterococcus |
0.010 |
0.013 |
0.007 |
prev_corn_genus$ITS %>%
rename(total = mean_relative_abundance_entire_dataset) %>%
distinct(Genus, Corn_Genotype, mean_relative_abundance, total) %>%
mutate(mean_relative_abundance= round(mean_relative_abundance, 3),
total = round(total, 3)) %>%
pivot_wider(names_from = Corn_Genotype, values_from = mean_relative_abundance) %>%
#filter(total >= 0.01) %>%
arrange(desc(total)) %>%
'Relative abundance B73', 'Relative abundance CML322')) %>%
| Genus |
Total Relative Abundance |
Relative abundance B73 |
Relative abundance CML322 |
| Aspergillus |
0.552 |
0.497 |
0.608 |
| Sarocladium |
0.252 |
0.226 |
0.279 |
| Meyerozyma |
0.124 |
0.148 |
0.101 |
| Talaromyces |
0.040 |
0.076 |
0.003 |
| Trichoderma |
0.013 |
0.026 |
0.001 |
| Penicillium |
0.006 |
0.011 |
0.001 |
| Curvularia |
0.005 |
0.007 |
0.003 |
| Alternaria |
0.004 |
0.007 |
0.002 |
| Ramichloridium |
0.001 |
0.001 |
0.001 |
| Acremonium |
0.000 |
0.001 |
0.000 |
| Bipolaris |
0.000 |
0.000 |
0.000 |
| Candida |
0.000 |
0.000 |
0.000 |
| Ceriporia |
0.000 |
0.000 |
0.000 |
| Cladosporium |
0.000 |
0.000 |
0.000 |
| Cyphellophora |
0.000 |
0.000 |
0.000 |
| Exserohilum |
0.000 |
0.000 |
0.000 |
| Fomes |
0.000 |
0.000 |
0.000 |
| Fusarium |
0.000 |
0.000 |
0.000 |
| Lecanicillium |
0.000 |
0.000 |
0.000 |
| Moesziomyces |
0.000 |
0.000 |
0.000 |
| Myrothecium |
0.000 |
0.001 |
0.000 |
| Paraconiothyrium |
0.000 |
0.000 |
0.000 |
| Peniophora |
0.000 |
0.000 |
0.000 |
| Peziza |
0.000 |
0.001 |
0.000 |
| Phanerochaete |
0.000 |
0.000 |
0.000 |
| Phlebia |
0.000 |
0.000 |
0.000 |
| Pyricularia |
0.000 |
0.000 |
0.000 |
| Scopuloides |
0.000 |
0.000 |
0.000 |
| Stereum |
0.000 |
0.001 |
0.000 |
| Tinctoporellus |
0.000 |
0.000 |
0.000 |
| Trametes |
0.000 |
0.000 |
0.000 |
| unidentified |
0.000 |
0.000 |
0.000 |
Most prevalent species
prev_corn_species = map(ps_species_ra,
function(ps_species_ra){
relative_counts = ps_species_ra %>% get_counts()
tax = ps_species_ra %>% get_tax()
relative_counts %>%
pivot_longer(-feature_id, names_to = 'SampleID', values_to = 'counts') %>%
mutate(prevalence_entire_dataset = sum(counts > 0) / n(),
mean_relative_abundance_entire_dataset = mean(counts)) %>%
group_by(Species, Corn_Genotype) %>%
summarize(prevalence = sum(counts > 0) / n(),
mean_relative_abundance = mean(counts),
prevalence_entire_dataset = first(prevalence_entire_dataset),
mean_relative_abundance_entire_dataset = first(mean_relative_abundance_entire_dataset)) %>%
ungroup()})
prev_corn_species$`16S` %>%
rename(total = mean_relative_abundance_entire_dataset) %>%
distinct(Species, Corn_Genotype, mean_relative_abundance, total) %>%
mutate(mean_relative_abundance= round(mean_relative_abundance, 3),
total = round(total, 3)) %>%
pivot_wider(names_from = Corn_Genotype, values_from = mean_relative_abundance) %>%
filter(total >= 0.01) %>%
arrange(desc(total)) %>%
kbl(format = 'html', col.names = c('Species', 'Total Relative Abundance',
'Relative abundance B73', 'Relative abundance CML322')) %>%
kable_classic(full_width = F, html_font = "Times New Roman")
| Species |
Total Relative Abundance |
Relative abundance B73 |
Relative abundance CML322 |
| Pantoea_ananatis |
0.193 |
0.207 |
0.173 |
| Burkholderia_gladioli |
0.152 |
0.195 |
0.091 |
| Listeria_grayi |
0.070 |
0.119 |
0.000 |
| Lactococcus_lactis |
0.068 |
0.077 |
0.056 |
| Sphingobacterium_siyangense |
0.050 |
0.001 |
0.120 |
| Lactococcus_garvieae |
0.043 |
0.073 |
0.000 |
| Sphingobacterium_thalpophilum |
0.032 |
0.000 |
0.079 |
| Sphingomonas_phyllosphaerae |
0.030 |
0.000 |
0.073 |
| Acinetobacter_baumannii |
0.029 |
0.000 |
0.070 |
| Pseudomonas_psychrotolerans |
0.026 |
0.008 |
0.052 |
| Comamonas_sediminis |
0.023 |
0.000 |
0.056 |
| Corynebacterium_kroppenstedtii |
0.021 |
0.036 |
0.000 |
| Flavobacterium_anatoliense |
0.019 |
0.000 |
0.047 |
| Sphingobacterium_multivorum |
0.015 |
0.012 |
0.019 |
| Staphylococcus_sciuri |
0.015 |
0.026 |
0.000 |
| Devosia_riboflavina |
0.014 |
0.000 |
0.035 |
| uncultured_Tistrella |
0.013 |
0.023 |
0.000 |
| uncultured_bacterium |
0.010 |
0.014 |
0.003 |
prev_corn_species$ITS %>%
rename(total = mean_relative_abundance_entire_dataset) %>%
distinct(Species, Corn_Genotype, mean_relative_abundance, total) %>%
mutate(mean_relative_abundance= round(mean_relative_abundance, 3),
total = round(total, 3)) %>%
pivot_wider(names_from = Corn_Genotype, values_from = mean_relative_abundance) %>%
filter(total >= 0.01) %>%
kbl(format = 'html', col.names = c('Species', 'Total Relative Abundance',
'Relative abundance B73', 'Relative abundance CML322')) %>%
| Species |
Total Relative Abundance |
Relative abundance B73 |
Relative abundance CML322 |
| Aspergillus_flavus |
0.567 |
0.525 |
0.608 |
| Sarocladium_zeae |
0.259 |
0.234 |
0.284 |
| Meyerozyma_caribbica |
0.135 |
0.168 |
0.102 |
| Aspergillus_niger |
0.011 |
0.021 |
0.000 |
| Talaromyces_purpureogenus |
0.010 |
0.019 |
0.000 |
reads = list('16S' = read_qza(here('data', '16S', 'rep-seqs-filtered.qza'))$data,
'ITS' = read_qza(here('data', 'ITS', 'rep-seqs-filtered.qza'))$data)
Below are barplots of relative taxon abundances for 16S sequencing
with samples grouped according to similarity using the neatmap
method.
## https://github.com/google/palette.js/blob/79a703df344e3b24380ce1a211a2df7f2d90ca22/palette.js#L802
mpn65 = c('#ff0029','#377eb8','#66a61e','#984ea3','#00d2d5','#ff7f00','#af8d00','#7f80cd','#b3e900','#c42e60','#a65628',
'#f781bf','#8dd3c7','#bebada','#fb8072','#80b1d3','#fdb462','#fccde5','#bc80bd','#ffed6f','#c4eaff','#cf8c00',
'#1b9e77','#d95f02','#e7298a','#e6ab02','#a6761d','#0097ff','#00d067','#000000','#252525','#525252','#737373',
'#969696','#bdbdbd','#f43600','#4ba93b','#5779bb','#927acc','#97ee3f','#bf3947','#9f5b00','#f48758','#8caed6',
'#cbe8ff','#fecddf','#c27eb6','#8cd2ce','#c4b8d9','#f883b0','#a49100','#f48800','#27d0df','#a04a9b')
map(rank_names(ps_genus$`16S`)[2:6], function(tax_rank){
df = ps_genus$`16S` %>%
speedyseq::mutate_sample_data(condition = condition_w_rep) %>%
transform(transform = "compositional") %>%
aggregate_rare(level = tax_rank, detection = 0.05, prevalence = 0.05)
scale_fill_manual(values=mpn65) +
theme(axis.text.x=element_text(angle=90,hjust=0, vjust=0.5)) +
labs(x = "Sample condition",
y = "Relative abundance",
title = glue("16S Relative abundance at {tax_rank} level"),
fill = tax_rank)
})
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
NA
The next two sets are done with ITS counts
but made the same way as above.
map(rank_names(ps_genus$ITS)[2:6], function(tax_rank){
speedyseq::mutate_sample_data(condition = condition_w_rep) %>%
transform(transform = "compositional") %>%
aggregate_rare(level = tax_rank, detection = 0.05, prevalence = 0.05)
p = plot_composition(df, x.label='condition', otu.sort = 'abundance', sample.sort='neatmap') +
guides(fill = guide_legend(ncol = 1)) +
scale_fill_manual(values=mpn65) +
labs(x = "Sample condition",
y = "Relative abundance",
title = glue("ITS Relative abundance at {tax_rank} level"),
p %>% plotly::ggplotly()
Warning: stress is (nearly) zero: you may have insufficient data
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
NA
prune_taxa(names(sort(taxa_sums(ps_genus_ra$`16S`),decreasing = TRUE)[1:30]), ps_genus_ra$`16S`) %>%
speedyseq::plot_heatmap(method = "NMDS", distance = "bray", sample.label = 'condition', taxa.label='Genus') +
strip.text.x = element_text(size = 16), plot.title = element_text(size=22)) +
labs(title = '16S Heatmap of top 30 most abundant genera')

prune_taxa(names(sort(taxa_sums(ps_genus_ra$ITS),decreasing = TRUE)[1:30]), ps_genus_ra$ITS) %>%
speedyseq::plot_heatmap(method = "NMDS", distance = "bray", sample.label = 'condition', taxa.label='Genus') +
strip.text.x = element_text(size = 16), plot.title = element_text(size=22)) +
labs(title = 'ITS Heatmap of top 30 most abundant genera')

save.image(here('src/16_and_ITS_import.RData'))
LS0tDQp0aXRsZTogIjE2UyBhbmQgSVRTIGRhdGEgaW1wb3J0Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICBlZGl0b3Jfb3B0aW9uczogDQogICAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQpsaWJyYXJ5KHFpaW1lMlIpDQpsaWJyYXJ5KGdnZWFzeSkNCmxpYnJhcnkocGh5bG9zZXEpDQpsaWJyYXJ5KEJpb3N0cmluZ3MpDQpsaWJyYXJ5KGdnc2lnbmlmKQ0KbGlicmFyeShnZ2Vhc3kpDQpsaWJyYXJ5KGdncHVicikNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShwYXRjaHdvcmspDQpsaWJyYXJ5KERFU2VxMikNCmxpYnJhcnkoQUxERXgyKQ0KbGlicmFyeShtaWNyb2Jpb21lKSANCmxpYnJhcnkodmVnYW4pDQpsaWJyYXJ5KEFOQ09NQkMpDQpsaWJyYXJ5KGdsdWUpDQpsaWJyYXJ5KGdnc3RhdHNwbG90KQ0KbGlicmFyeShiZWVzd2FybSkNCmxpYnJhcnkobXNhKQ0KbGlicmFyeShoZXJlKQ0KbGlicmFyeShnZ3RleHQpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpsaWJyYXJ5KHRpZHlIZWF0bWFwKQ0KbGlicmFyeShTcGllY0Vhc2kpDQpsaWJyYXJ5KE5ldENvTWkpDQpsaWJyYXJ5KGlncmFwaCkNCmxpYnJhcnkodGlkeWdyYXBoKQ0KbGlicmFyeShnZ3JhcGgpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcucmV0aW5hID0gMSwgZHBpPTQ1MCkNCm9wdGlvbnMoZHBseXIuc3VtbWFyaXNlLmluZm9ybT1GKQ0KdGhlbWVfc2V0KHRoZW1lX2J3KCkpDQpzZXNzaW9uSW5mbygpICU+JQ0KICBjYXB0dXJlLm91dHB1dChzZXNzaW9uSW5mbygpKSAlPiUNCiAgd3JpdGVfbGluZXMoaGVyZSgnb3V0cHV0L3Nlc3Npb25faW5mby50eHQnKSkNCmBgYA0KDQpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9DQptZXRhZGF0YSA9IHJlYWRfdHN2KGhlcmUoJ2RhdGEnLCAnbWV0YWRhdGFfbWVyZ2VkLnRzdicpLCBjb2xfdHlwZXMgPSBjb2xzKCkpICU+JQ0KICByZW5hbWUoVGltZVBvaW50ID0gVGltZXBvaW50KSAlPiUNCiAgbXV0YXRlKFRpbWVQb2ludCA9IHN0cl9yZXBsYWNlKFRpbWVQb2ludCwgJ14nLCAnVCcpLA0KICAgICAgICAgY29uZGl0aW9uID0gc3RyX2MoQ29yblZhcmlldHksIEZ1bmdhbFN0cmFpbiwgVGlzc3VlRXh0cmFjdGlvbiwgc2VwPSdfJykpICU+JQ0KICByZW5hbWUoQ29ybl9HZW5vdHlwZSA9IENvcm5WYXJpZXR5LCBGdW5nYWxfVHJlYXRtZW50ID0gRnVuZ2FsU3RyYWluLCBUaXNzdWVfRXh0cmFjdGlvbiA9IFRpc3N1ZUV4dHJhY3Rpb24pDQpgYGANCg0KYGBge3J9DQpnZXRfY291bnRzID0gZnVuY3Rpb24oLikgb3R1X3RhYmxlKC4pICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbignZmVhdHVyZV9pZCcpICU+JSBhc190aWJibGUoKQ0KZ2V0X3RheCA9IGZ1bmN0aW9uKC4pIHRheF90YWJsZSguKSAlPiUgZGF0YS5mcmFtZSgpICU+JSByb3duYW1lc190b19jb2x1bW4oJ2ZlYXR1cmVfaWQnKSAlPiUgYXNfdGliYmxlKCkNCmdldF9xemEgPSBmdW5jdGlvbihmaWxlcGF0aCl7cmVhZF9xemEoZmlsZXBhdGgpJGRhdGEgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCdTYW1wbGVJRCcpfQ0KZ2V0X3NhbXBsZV9kYXRhID0gZnVuY3Rpb24oLikgZGF0YS5mcmFtZShzYW1wbGVfZGF0YSguKSkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbignU2FtcGxlSUQnKSAlPiUgYXNfdGliYmxlKCkNCmNoYW5nZV9pdHNfaWRzX3RvX21hdGNoXzE2UyA9IC4gJT4lIA0KICBtdXRhdGUoU2FtcGxlSUQgPSBzdHJfcmVwbGFjZShTYW1wbGVJRCwgJyguKj8pLSguKiknLCAnXFwyLVxcMScpLA0KICAgICAgICAgU2FtcGxlSUQgPSBzdHJfcmVwbGFjZV9hbGwoU2FtcGxlSUQsIGMoJy1CNzMnPSctMzczJywgJy1DMzIyJz0nLTMyMicpKSkNCmNoYW5nZV8xNlNfaWRzX3RvX21hdGNoX0lUUyA9IC4gJT4lIA0KICBtdXRhdGUoU2FtcGxlSUQgPSBzdHJfcmVwbGFjZShTYW1wbGVJRCwgJyguKj8pLSguKiknLCAnXFwyLVxcMScpLA0KICAgICAgICAgU2FtcGxlSUQgPSBzdHJfcmVwbGFjZV9hbGwoU2FtcGxlSUQsIGMoJy0zNzMnPSctQjczJywgJy0zMjInPSctQzMyMicpKSkNCmBgYA0KDQpgYGB7cn0NCnBzID0gbGlzdCgnMTZTJz0gcXphX3RvX3BoeWxvc2VxKGZlYXR1cmVzID0gaGVyZSgnZGF0YScsICcxNlMnLCAndGFibGVfd29fb3V0bGllcnMucXphJyksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRyZWU9aGVyZSgnZGF0YScsICcxNlMnLCAncm9vdGVkLXRyZWUucXphJyksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRheG9ub215PWhlcmUoJ2RhdGEnLCAnMTZTJywgJ21lcmdlZC10YXhvbm9teS5xemEnKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGE9aGVyZSgnZGF0YScsICcxNlMnLCAnbWV0YWRhdGFfZmlsdGVyZWQudHN2JyksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRtcD0nQzovVXNlcnMvYnJpYW4ubWFjay9Eb3dubG9hZHMvdG1wJykgJT4lIA0KICAgICAgICAgICAgc3Vic2V0X3NhbXBsZXMoQ29yblZhcmlldHkgIT0gJ2R1bW15JyAmIFRpbWVwb2ludCA9PSAnMicgJiBUaXNzdWVUeXBlID09ICdPdnVsZScpICU+JQ0KICAgICAgICAgICAgcHJ1bmVfdGF4YSh0YXhhX3N1bXMoLikgPiAwLCAuKSwNCiAgICAgICAgICAnSVRTJyA9IHF6YV90b19waHlsb3NlcShmZWF0dXJlcyA9IGhlcmUoJ2RhdGEnLCAnSVRTJywgJ3RhYmxlLW5vLW1pdG9jaG9uZHJpYS1uby1jaGxvcm9wbGFzdC5xemEnKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgdHJlZT1oZXJlKCdkYXRhJywgJ0lUUycsICdyb290ZWQtdHJlZS5xemEnKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgdGF4b25vbXk9aGVyZSgnZGF0YScsICdJVFMnLCAnbWVyZ2VkLXRheG9ub215LnF6YScpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhZGF0YT1oZXJlKCdkYXRhJywgJ0lUUycsICdtZXRhZGF0YV9tZXJnZWQudHN2JyksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRtcD0nQzovVXNlcnMvYnJpYW4ubWFjay9Eb3dubG9hZHMvdG1wJykgICU+JSANCiAgICAgICAgICAgIHN1YnNldF9zYW1wbGVzKENvcm5WYXJpZXR5ICAhPSAnZHVtbXknICYgVGltZXBvaW50ID09ICcyJyAmIFRpc3N1ZVR5cGUgPT0gJ092dWxlJykgJT4lDQogICAgICAgICAgICBwcnVuZV90YXhhKHRheGFfc3VtcyguKSA+IDAsIC4pDQogICAgICAgICAgICApDQpwcyA9IG1hcChwcywgZnVuY3Rpb24oLngpew0KICBzYW1wbGVfZGF0YSgueCkgPSBnZXRfc2FtcGxlX2RhdGEoLngpICU+JSANCiAgICByZW5hbWUoVGltZVBvaW50ID0gVGltZXBvaW50KSAlPiUNCiAgICBtdXRhdGUoVGltZVBvaW50ID0gc3RyX3JlcGxhY2UoVGltZVBvaW50LCAnXicsICdUJyksDQogICAgICAgICAgIGNvbmRpdGlvbiA9IHN0cl9jKENvcm5WYXJpZXR5LCBGdW5nYWxTdHJhaW4sIFRpc3N1ZUV4dHJhY3Rpb24sIHNlcD0nXycpKSAlPiUNCiAgICByZW5hbWUoQ29ybl9HZW5vdHlwZSA9IENvcm5WYXJpZXR5LCBGdW5nYWxfVHJlYXRtZW50ID0gRnVuZ2FsU3RyYWluLCBUaXNzdWVfRXh0cmFjdGlvbiA9IFRpc3N1ZUV4dHJhY3Rpb24pICU+JQ0KICAgIGdyb3VwX2J5KGNvbmRpdGlvbikgJT4lDQogICAgbXV0YXRlKGJpb2xvZ2ljYWxfcmVwID0gcm93X251bWJlcigpKSAlPiUNCiAgICB1bmdyb3VwKCkgJT4lDQogICAgbXV0YXRlKGNvbmRpdGlvbl93X3JlcCA9IHN0cl9jKGNvbmRpdGlvbiwgYmlvbG9naWNhbF9yZXAsIHNlcD0nXycpKSAlPiUNCiAgICBjb2x1bW5fdG9fcm93bmFtZXMoJ1NhbXBsZUlEJykNCiAgcmV0dXJuKC54KX0pDQpzYW1wbGVfZGF0YShwcyRJVFMpID0gZ2V0X3NhbXBsZV9kYXRhKHBzJElUUykgJT4lDQogIGNoYW5nZV9pdHNfaWRzX3RvX21hdGNoXzE2UygpICU+JQ0KICBjb2x1bW5fdG9fcm93bmFtZXMoJ1NhbXBsZUlEJykNCnBzICU+JSBpbWFwKH5nZXRfY291bnRzKC54KSAlPiUgDQogICAgICAgICAgICAgIGxlZnRfam9pbihnZXRfdGF4KC54KSwgYnk9J2ZlYXR1cmVfaWQnKSAlPiUNCiAgICAgICAgICAgICAgcmVuYW1lKGFsbF9vZihnZXRfc2FtcGxlX2RhdGEoLngpICU+JSBwdWxsKFNhbXBsZUlELCBuYW1lPWNvbmRpdGlvbl93X3JlcCkpKSAlPiUNCiAgICAgICAgICAgICAgd3JpdGVfY3N2KGhlcmUoc3RyX2dsdWUoJ291dHB1dC9yYXdfY291bnRzX2FuZF90YXhvbm9teV97Lnl9LmNzdicpKSkNCiAgICAgICAgICAgICAgKQ0KYGBgDQoNClJlbW92ZWQgdGF4YSBub3QgYXNzaWduZWQgdG8gYSBwaHlsdW0uIEFmdGVyIHJlbW92aW5nIHRoZXNlIHRheGEsIDEgMTZTIHNhbXBsZXMgd2FzIHJlbW92ZWQgZHVlIHRvIGhhdmluZyB0b3RhbCBjb3VudHMgPD0gMTUwMC4NCg0KYGBge3J9DQpwc19waHlsdW1fZmlsdCA9IGxpc3QoJzE2UycgPSBzdWJzZXRfdGF4YShwcyRgMTZTYCwgIWlzLm5hKFBoeWx1bSkgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIVBoeWx1bSAlaW4lIGMoJycsICd1bmNoYXJhY3Rlcml6ZWQnLCAndW5pZGVudGlmaWVkJykgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIUtpbmdkb20gJWluJSBjKCdkX19FdWthcnlvdGEnKSkgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgcHJ1bmVfc2FtcGxlcyhzYW1wbGVfc3VtcyguKSA+PSAxNTAwLCAuKSAlPiUgcHJ1bmVfdGF4YSh0YXhhX3N1bXMoLikgPiAwLCAuKSwNCiAgICAgICAgICAgICAgICAgICAgICAnSVRTJyA9IHN1YnNldF90YXhhKHBzJElUUywgIWlzLm5hKFBoeWx1bSkgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhUGh5bHVtICVpbiUgYygiIiwgJ3VuY2hhcmFjdGVyaXplZCcsICd1bmlkZW50aWZpZWQnKSkgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgcHJ1bmVfc2FtcGxlcyhzYW1wbGVfc3VtcyguKSA+PSAxNTAwLCAuKSAlPiUgcHJ1bmVfdGF4YSh0YXhhX3N1bXMoLikgPiAwLCAuKSkNCnBzX3BoeWx1bV9maWx0X3RheCA9IHBzX3BoeWx1bV9maWx0ICU+JSBtYXAoZ2V0X3RheCkNCnBzX3BoeWx1bV9maWx0X2NvdW50cyA9IHBzX3BoeWx1bV9maWx0ICU+JSBtYXAoZ2V0X2NvdW50cykNCnBzX3BoeWx1bV9maWx0X3JhID0gbWFwKHBzX3BoeWx1bV9maWx0LCB+dHJhbnNmb3JtX3NhbXBsZV9jb3VudHMoLngsIGZ1bmN0aW9uKHgpe3ggLyBzdW0oeCl9KSkNCnBzX3BoeWx1bV9maWx0DQpgYGANCmBgYHtyfQ0KcHNfcGh5bHVtX2ZpbHQgJT4lIGltYXAoZnVuY3Rpb24ocHNfcGh5bHVtX2ZpbHQsIHNlcV90eXBlKXsNCiAgcHNfcGh5bHVtX2ZpbHQgJT4lIGdldF90YXgoKSAlPiUNCiAgICBzdW1tYXJpemUoYWNyb3NzKC1mZWF0dXJlX2lkLCB+c3VtKCFpcy5uYSgueCkpLyBuKCkpKSAlPiUNCiAgICByb3VuZCgyKSAlPiUNCiAgICBwaXZvdF9sb25nZXIoZXZlcnl0aGluZygpLCBuYW1lc190byA9ICdUYXhvbm9taWMgcmFuaycsIHZhbHVlc190byA9IHNlcV90eXBlKQ0KICB9KSAlPiUNCiAgcmVkdWNlKGZ1bGxfam9pbiwgYnk9J1RheG9ub21pYyByYW5rJykgJT4lDQogIGtibChmb3JtYXQgPSAnaHRtbCcsIGNhcHRpb249J0ZyYWN0aW9uIG9mIEFTVnMgY2xhc3NpZmllZCBhdCBlYWNoIHJhbmsnKSAlPiUNCiAga2FibGVfY2xhc3NpYyhmdWxsX3dpZHRoID0gRiwgaHRtbF9mb250ID0gIlRpbWVzIE5ldyBSb21hbiIpIA0KYGBgDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTZ9DQpwc19waHlsdW1fZmlsdCAlPiUgaW1hcChmdW5jdGlvbihwc19waHlsdW1fZmlsdCwgc2VxX3R5cGUpew0KICBwc19waHlsdW1fZmlsdCAlPiUgc2FtcGxlX3N1bXMoKSAlPiUgZW5mcmFtZShuYW1lID0gJ1NhbXBsZUlEJywgdmFsdWUgPSAndG90YWxfY291bnRzJykgJT4lDQogICAgbGVmdF9qb2luKG1ldGFkYXRhLCBieT0nU2FtcGxlSUQnKSAlPiUNCiAgICBtdXRhdGUoc2VxX3R5cGUgPSBzZXFfdHlwZSkNCiAgfSkgJT4lDQogIGJpbmRfcm93cygpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9Y29uZGl0aW9uLCB5PXRvdGFsX2NvdW50cykpICsNCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGUgPSBOQSkgKw0KICBnZ2JlZXN3YXJtOjpnZW9tX3F1YXNpcmFuZG9tKGFscGhhID0gMC4zLCB3aWR0aD0wLjIsIGdyb3VwT25YPVRSVUUpICsNCiAgZmFjZXRfd3JhcCh+c2VxX3R5cGUsIG5jb2w9MSwgc2NhbGVzID0gJ2ZyZWUnKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKSArDQogIGxhYnModGl0bGU9J1RvdGFsIG51bWJlciBvZiBjb3VudHMgZm9yIGVhY2ggc2FtcGxlJykgKw0KICBnZ2Vhc3k6OmVhc3lfcm90YXRlX3hfbGFiZWxzKCkgKw0KICBnZ2Vhc3k6OmVhc3lfY2VudGVyX3RpdGxlKCkNCmBgYA0KDQo8YnI+PGJyPjxicj4NCg0KUmFyZWZ5aW5nIHRvIDE1NjkgY291bnRzLg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCnBzX3BoeWx1bV9maWx0X3JhcmVmaWVkID0gIHBzX3BoeWx1bV9maWx0ICU+JSBtYXAoDQogIH5yYXJlZnlfZXZlbl9kZXB0aCgueCwgc2FtcGxlLnNpemUgPSAxNTY5LCBybmdzZWVkPTExMDAgLCByZXBsYWNlPUZBTFNFKSAlPiUNCiAgICAgcHJ1bmVfdGF4YSh0YXhhX3N1bXMoLikgPiAwLCAuKSkNCnBzX3BoeWx1bV9maWx0X3JhcmVmaWVkDQpgYGANCg0KPGJyPjxicj48YnI+PGJyPjxicj48YnI+DQoNCmBgYHtyLCBmaWcud2lkdGg9MTJ9DQpwcmV2ID0gbWFwKHBzX3BoeWx1bV9maWx0X3JhLA0KICAgICAgICAgICAgZnVuY3Rpb24ocHNfcGh5bHVtX2ZpbHRfcmEpew0KICAgICAgICAgICAgICByZWxhdGl2ZV9jb3VudHMgPSBwc19waHlsdW1fZmlsdF9yYSAlPiUgZ2V0X2NvdW50cygpDQogICAgICAgICAgICAgIHRheCA9IHBzX3BoeWx1bV9maWx0X3JhICU+JSBnZXRfdGF4KCkNCiAgICAgICAgICAgICAgcmVsYXRpdmVfY291bnRzICU+JSANCiAgICAgICAgICAgICAgICByZWZyYW1lKGZlYXR1cmVfaWQgPSBmZWF0dXJlX2lkLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHByZXZhbGVuY2UgPSByb3dTdW1zKC4+IDApIC0xLCAjc3VidHJhY3RlZCAxIGJlY2F1c2UgZmVhdHVyZV9pZCBjb2x1bW4gaXMgYWx3YXlzIGNvdW50ZWQNCiAgICAgICAgICAgICAgICAgICAgICAgICB0b3RhbF9yZWxhdGl2ZV9hYnVuZGFuY2UgPSB0YXhhX3N1bXMocHNfcGh5bHVtX2ZpbHRfcmEpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBQaHlsdW0gPSB0YXgkUGh5bHVtLA0KICAgICAgICAgICAgICAgICAgICAgICAgIEdlbnVzID0gdGF4JEdlbnVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgIFNwZWNpZXMgPSB0YXgkU3BlY2llcyl9KQ0KcHJldl9wbG90cyA9IHByZXYgJT4lIGltYXAoZnVuY3Rpb24ocHJldiwgc2VxX3R5cGUpew0KICBwcmV2ICU+JSANCiAgICBtdXRhdGUocHJldmFsZW5jZSA9IHByZXZhbGVuY2UgLyBuc2FtcGxlcyhwc19waHlsdW1fZmlsdFtbc2VxX3R5cGVdXSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHRvdGFsX3JlbGF0aXZlX2FidW5kYW5jZSwgcHJldmFsZW5jZSwgY29sb3I9UGh5bHVtLCB0ZXh0PWdsdWUoJ0dlbnVzOiB7R2VudXN9PGJyPlNwZWNpZXM6IHtTcGVjaWVzfScpKSkgKw0KICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAuMDUsIGFscGhhID0gMC41LCBsaW5ldHlwZSA9IDIpICsgDQogICAgZ2VvbV9wb2ludChzaXplID0gMiwgYWxwaGEgPSAwLjYpICsNCiAgICBzY2FsZV94X2xvZzEwKCkgKyB4bGFiKCJUb3RhbCBSZWxhdGl2ZSBBYnVuZGFuY2UiKSArIHlsYWIoIlByZXZhbGVuY2UgW0ZyYWN0aW9uIFNhbXBsZXNdIikgKw0KICAgIGZhY2V0X3dyYXAoflBoeWx1bSkgKw0KICAgIGxhYnModGl0bGUgPSBnbHVlKCd7c2VxX3R5cGV9IEFTViBQcmV2YWxlbmNlJykpICsNCiAgICB0aGVtZV9ncmF5KCkgKw0KICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIil9KQ0KYGBgDQoNCmBgYHtyLCBvdXQud2lkdGg9MTJ9DQpwcmV2X3Bsb3RzJGAxNlNgICU+JSBwbG90bHk6OmdncGxvdGx5KCkNCmBgYA0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00fQ0KcHJldl9wbG90cyRJVFMgJT4lIHBsb3RseTo6Z2dwbG90bHkoKQ0KYGBgDQpQcmVkb21pbmFudCBwaHlsYQ0KYGBge3J9DQpwc19waHlsYSA9IG1hcChwc19waHlsdW1fZmlsdCwgDQogICAgICAgICAgICAgICBmdW5jdGlvbihwc19waHlsdW1fZmlsdCl7DQogICAgICAgICAgICAgICAgIHBzX3BoeWxhID0gdGF4X2dsb20ocHNfcGh5bHVtX2ZpbHQsICJQaHlsdW0iLCBOQXJtID0gVFJVRSkNCiAgICAgICAgICAgICAgICAgcHJldmFsZW5jZVRocmVzaG9sZCA9ICAwLjA1ICogbnNhbXBsZXMocHNfcGh5bGEpDQogICAgICAgICAgICAgICAgIHBzX3BoeWxhID0gcHNfcGh5bGEgJT4lIA0KICAgICAgICAgICAgICAgICAgIGdldF9jb3VudHMoKSAlPiUgDQogICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKGZlYXR1cmVfaWQgPSBmZWF0dXJlX2lkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmV2YWxlbmNlID0gcm93U3VtcyguPiAwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVG90YWxBYnVuZGFuY2UgPSB0YXhhX3N1bXMocHNfcGh5bGEpKSAlPiUNCiAgICAgICAgICAgICAgICAgICBmaWx0ZXIocHJldmFsZW5jZSA+PSBwcmV2YWxlbmNlVGhyZXNob2xkKSAlPiUgDQogICAgICAgICAgICAgICAgICAgcHVsbChmZWF0dXJlX2lkKSAlPiUNCiAgICAgICAgICAgICAgICAgICBwcnVuZV90YXhhKC4sIHBzX3BoeWxhKSAlPiUNCiAgICAgICAgICAgICAgICAgICBwcnVuZV9zYW1wbGVzKHNhbXBsZV9zdW1zKC4pID49IDUwMCwgLikgJT4lIA0KICAgICAgICAgICAgICAgICAgIHBydW5lX3RheGEodGF4YV9zdW1zKC4pID4gMCwgLikNCiAgICAgICAgICAgICAgICAgc2FtcGxlX2RhdGEocHNfcGh5bGEpID0gc2FtcGxlX2RhdGEocHNfcGh5bGEpICU+JSANCiAgICAgICAgICAgICAgICAgICBhcygnZGF0YS5mcmFtZScpICU+JQ0KICAgICAgICAgICAgICAgICAgIG11dGF0ZShUaXNzdWVUeXBlID0gZmN0X3JlbGV2ZWwoVGlzc3VlVHlwZSwgJ092dWxlJykpDQogICAgICAgICAgICAgICAgIHJldHVybihwc19waHlsYSl9KQ0KcHNfcGh5bGFfcmEgPSBtYXAocHNfcGh5bGEsIH50cmFuc2Zvcm1fc2FtcGxlX2NvdW50cygueCwgZnVuY3Rpb24oeCl7eCAvIHN1bSh4KX0pKQ0KcHJldl9jb3JuX3BoeWxhID0gbWFwKHBzX3BoeWxhX3JhLA0KICAgICAgICAgICAgZnVuY3Rpb24ocHNfcGh5bGFfcmEpew0KICAgICAgICAgICAgcmVsYXRpdmVfY291bnRzID0gcHNfcGh5bGFfcmEgJT4lIGdldF9jb3VudHMoKQ0KICAgICAgICAgICAgICB0YXggPSBwc19waHlsYV9yYSAlPiUgZ2V0X3RheCgpDQogICAgICAgICAgICAgIHJlbGF0aXZlX2NvdW50cyAlPiUgDQogICAgICAgICAgICAgICAgcGl2b3RfbG9uZ2VyKC1mZWF0dXJlX2lkLCBuYW1lc190byA9ICdTYW1wbGVJRCcsIHZhbHVlc190byA9ICdjb3VudHMnKSAlPiUNCiAgICAgICAgICAgICAgICBsZWZ0X2pvaW4obWV0YWRhdGEsIGJ5PSdTYW1wbGVJRCcpICU+JQ0KICAgICAgICAgICAgICAgIGxlZnRfam9pbih0YXgsIGJ5PSdmZWF0dXJlX2lkJykgJT4lDQogICAgICAgICAgICAgICAgZ3JvdXBfYnkoUGh5bHVtKSAlPiUNCiAgICAgICAgICAgICAgICBtdXRhdGUocHJldmFsZW5jZV9lbnRpcmVfZGF0YXNldCA9IHN1bShjb3VudHMgPiAwKSAvIG4oKSwNCiAgICAgICAgICAgICAgICAgICAgICAgbWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2VfZW50aXJlX2RhdGFzZXQgPSBtZWFuKGNvdW50cykpICU+JQ0KICAgICAgICAgICAgICAgIGdyb3VwX2J5KFBoeWx1bSwgQ29ybl9HZW5vdHlwZSkgJT4lDQogICAgICAgICAgICAgICAgc3VtbWFyaXplKHByZXZhbGVuY2UgPSBzdW0oY291bnRzID4gMCkgLyBuKCksDQogICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlID0gbWVhbihjb3VudHMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBwcmV2YWxlbmNlX2VudGlyZV9kYXRhc2V0ID0gZmlyc3QocHJldmFsZW5jZV9lbnRpcmVfZGF0YXNldCksDQogICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlX2VudGlyZV9kYXRhc2V0ID0gZmlyc3QobWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2VfZW50aXJlX2RhdGFzZXQpKSAlPiUNCiAgICAgICAgICAgICAgICB1bmdyb3VwKCl9KQ0KcHJldl9jb3JuX3BoeWxhJGAxNlNgICU+JSANCiAgcmVuYW1lKHRvdGFsID0gbWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2VfZW50aXJlX2RhdGFzZXQpICU+JQ0KICBkaXN0aW5jdChQaHlsdW0sIENvcm5fR2Vub3R5cGUsIG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlLCB0b3RhbCkgJT4lDQogIG11dGF0ZShtZWFuX3JlbGF0aXZlX2FidW5kYW5jZT0gcm91bmQobWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2UsIDMpLA0KICAgICAgICAgdG90YWwgPSByb3VuZCh0b3RhbCwgMykpICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gQ29ybl9HZW5vdHlwZSwgdmFsdWVzX2Zyb20gPSBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZSkgJT4lDQogIGZpbHRlcih0b3RhbCA+PSAwLjAxKSAlPiUNCiAgYXJyYW5nZShkZXNjKHRvdGFsKSkgJT4lDQogIGtibChmb3JtYXQgPSAnaHRtbCcsIGNvbC5uYW1lcyA9IGMoJ1BoeWx1bScsICdUb3RhbCBSZWxhdGl2ZSBBYnVuZGFuY2UnLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnUmVsYXRpdmUgYWJ1bmRhbmNlIEI3MycsICdSZWxhdGl2ZSBhYnVuZGFuY2UgQ01MMzIyJykpICU+JQ0KICBrYWJsZV9jbGFzc2ljKGZ1bGxfd2lkdGggPSBGLCBodG1sX2ZvbnQgPSAiVGltZXMgTmV3IFJvbWFuIikNCnByZXZfY29ybl9waHlsYSRJVFMgJT4lIA0KICByZW5hbWUodG90YWwgPSBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZV9lbnRpcmVfZGF0YXNldCkgJT4lDQogIGRpc3RpbmN0KFBoeWx1bSwgQ29ybl9HZW5vdHlwZSwgbWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2UsIHRvdGFsKSAlPiUNCiAgbXV0YXRlKG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlPSByb3VuZChtZWFuX3JlbGF0aXZlX2FidW5kYW5jZSwgMyksDQogICAgICAgICB0b3RhbCA9IHJvdW5kKHRvdGFsLCAzKSkgJT4lDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBDb3JuX0dlbm90eXBlLCB2YWx1ZXNfZnJvbSA9IG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlKSAlPiUNCiAgI2ZpbHRlcih0b3RhbCA+PSAwLjAxKSAlPiUNCiAgYXJyYW5nZShkZXNjKHRvdGFsKSkgJT4lDQogIGtibChmb3JtYXQgPSAnaHRtbCcsIGNvbC5uYW1lcyA9IGMoJ1BoeWx1bScsICdUb3RhbCBSZWxhdGl2ZSBBYnVuZGFuY2UnLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnUmVsYXRpdmUgYWJ1bmRhbmNlIEI3MycsICdSZWxhdGl2ZSBhYnVuZGFuY2UgQ01MMzIyJykpICU+JQ0KICBrYWJsZV9jbGFzc2ljKGZ1bGxfd2lkdGggPSBGLCBodG1sX2ZvbnQgPSAiVGltZXMgTmV3IFJvbWFuIikNCmBgYA0KPGJyPjxicj48YnI+IFJlbW92ZWQgdGF4YSB0aGF0IGFyZSBwcmVzZW50IGluIGxlc3MgdGhhbiA1JSBvZiBzYW1wbGVzIGZvciBBU1YgbGV2ZWwgZGF0YXNldC4gVGhpcyB3aWxsIGJlIHVzZWQgZm9yIGRpZmZlcmVudGlhbCBhYnVuZGFuY2UgdGVzdGluZyBhdCBBU1YgbGV2ZWwuDQpgYGB7cn0NCnBzX3ByZXZmID0gbWFwMihwc19waHlsdW1fZmlsdCwgcHJldiwNCiAgICAgICAgICAgICAgIGZ1bmN0aW9uKHBzX3BoeWx1bV9maWx0LCBwcmV2KXsNCiAgICAgICAgICAgICAgICAgcHJldmFsZW5jZVRocmVzaG9sZCA9ICAwLjA1ICogbnNhbXBsZXMocHNfcGh5bHVtX2ZpbHQpDQogICAgICAgICAgICAgICAgIGtlZXBUYXhhID0gZmlsdGVyKHByZXYsIHByZXZhbGVuY2UgPj0gcHJldmFsZW5jZVRocmVzaG9sZCkgJT4lIA0KICAgICAgICAgICAgICAgICAgIHB1bGwoZmVhdHVyZV9pZCkNCiAgICAgICAgICAgICAgICAgcHJ1bmVfdGF4YShrZWVwVGF4YSwgcHNfcGh5bHVtX2ZpbHQpICU+JQ0KICAgICAgICAgICAgICAgICAgIHBydW5lX3NhbXBsZXMoc2FtcGxlX3N1bXMoLikgPj0gNTAwLCAuKSAlPiUgDQogICAgICAgICAgICAgICAgICAgcHJ1bmVfdGF4YSh0YXhhX3N1bXMoLikgPiAwLCAuKX0pDQpwc19wcmV2Zl9yYSA9IG1hcChwc19wcmV2ZiwgfnRyYW5zZm9ybV9zYW1wbGVfY291bnRzKC54LCBmdW5jdGlvbih4KXt4IC8gc3VtKHgpfSkpDQpwc19wcmV2Zl9jbHIgPSBtYXAocHNfcHJldmYsIG1pY3JvYmlvbWU6OnRyYW5zZm9ybSwgJ2NscicpDQpwc19wcmV2Zl9hbHIgPSBtYXAocHNfcHJldmYsIG1pY3JvYmlvbWU6OnRyYW5zZm9ybSwgJ2FscicsIHNoaWZ0PTEpDQpwc19wcmV2Zg0KYGBgDQoNCjxicj48YnI+PGJyPiBBZ2dsb21lcmF0ZWQgY291bnRzIGF0IGJvdGggZ2VudXMgbGV2ZWwgYW5kIHNwZWNpZXMgbGV2ZWwuDQoNCmBgYHtyfQ0KcHNfZ2VudXMgPSBtYXAocHNfcGh5bHVtX2ZpbHQsIA0KICAgICAgICAgICAgICAgZnVuY3Rpb24ocHNfcGh5bHVtX2ZpbHQpew0KICAgICAgICAgICAgICAgICBwc19nZW51cyA9IHRheF9nbG9tKHBzX3BoeWx1bV9maWx0LCAiR2VudXMiLCBOQXJtID0gVFJVRSkNCiAgICAgICAgICAgICAgICAgcHJldmFsZW5jZVRocmVzaG9sZCA9ICAwLjA1ICogbnNhbXBsZXMocHNfZ2VudXMpDQogICAgICAgICAgICAgICAgIHBzX2dlbnVzID0gcHNfZ2VudXMgJT4lIA0KICAgICAgICAgICAgICAgICAgIGdldF9jb3VudHMoKSAlPiUgDQogICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKGZlYXR1cmVfaWQgPSBmZWF0dXJlX2lkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmV2YWxlbmNlID0gcm93U3VtcyguPiAwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVG90YWxBYnVuZGFuY2UgPSB0YXhhX3N1bXMocHNfZ2VudXMpKSAlPiUNCiAgICAgICAgICAgICAgICAgICBmaWx0ZXIocHJldmFsZW5jZSA+PSBwcmV2YWxlbmNlVGhyZXNob2xkKSAlPiUgDQogICAgICAgICAgICAgICAgICAgcHVsbChmZWF0dXJlX2lkKSAlPiUNCiAgICAgICAgICAgICAgICAgICBwcnVuZV90YXhhKC4sIHBzX2dlbnVzKSAlPiUNCiAgICAgICAgICAgICAgICAgICBwcnVuZV9zYW1wbGVzKHNhbXBsZV9zdW1zKC4pID49IDUwMCwgLikgJT4lIA0KICAgICAgICAgICAgICAgICAgIHBydW5lX3RheGEodGF4YV9zdW1zKC4pID4gMCwgLikNCiAgICAgICAgICAgICAgICAgc2FtcGxlX2RhdGEocHNfZ2VudXMpID0gc2FtcGxlX2RhdGEocHNfZ2VudXMpICU+JSANCiAgICAgICAgICAgICAgICAgICBhcygnZGF0YS5mcmFtZScpICU+JQ0KICAgICAgICAgICAgICAgICAgIG11dGF0ZShUaXNzdWVUeXBlID0gZmN0X3JlbGV2ZWwoVGlzc3VlVHlwZSwgJ092dWxlJykpDQogICAgICAgICAgICAgICAgIHJldHVybihwc19nZW51cyl9KQ0KcHNfZ2VudXNfcmEgPSBtYXAocHNfZ2VudXMsIH50cmFuc2Zvcm1fc2FtcGxlX2NvdW50cygueCwgZnVuY3Rpb24oeCl7eCAvIHN1bSh4KX0pKQ0KcHNfZ2VudXNfY2xyID0gbWFwKHBzX2dlbnVzLCBtaWNyb2Jpb21lOjp0cmFuc2Zvcm0sICdjbHInKQ0KcHNfZ2VudXNfYWxyID0gbWFwKHBzX2dlbnVzLCBtaWNyb2Jpb21lOjp0cmFuc2Zvcm0sICdhbHInLCBzaGlmdD0xKQ0KcHNfc3BlY2llcyA9IG1hcChwc19waHlsdW1fZmlsdCwgDQogICAgICAgICAgICAgICBmdW5jdGlvbihwc19waHlsdW1fZmlsdCl7DQogICAgICAgICAgICAgICAgIHBzX3NwZWNpZXMgPSB0YXhfZ2xvbShwc19waHlsdW1fZmlsdCwgIlNwZWNpZXMiLCBOQXJtID0gVFJVRSkNCiAgICAgICAgICAgICAgICAgcHJldmFsZW5jZVRocmVzaG9sZCA9ICAwLjA1ICogbnNhbXBsZXMocHNfc3BlY2llcykNCiAgICAgICAgICAgICAgICAgcHNfc3BlY2llcyA9IHBzX3NwZWNpZXMgJT4lIA0KICAgICAgICAgICAgICAgICAgIGdldF9jb3VudHMoKSAlPiUgDQogICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKGZlYXR1cmVfaWQgPSBmZWF0dXJlX2lkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmV2YWxlbmNlID0gcm93U3VtcyguPiAwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVG90YWxBYnVuZGFuY2UgPSB0YXhhX3N1bXMocHNfc3BlY2llcykpICU+JQ0KICAgICAgICAgICAgICAgICAgIGZpbHRlcihwcmV2YWxlbmNlID49IHByZXZhbGVuY2VUaHJlc2hvbGQpICU+JSANCiAgICAgICAgICAgICAgICAgICBwdWxsKGZlYXR1cmVfaWQpICU+JQ0KICAgICAgICAgICAgICAgICAgIHBydW5lX3RheGEoLiwgcHNfc3BlY2llcykgJT4lDQogICAgICAgICAgICAgICAgICAgcHJ1bmVfc2FtcGxlcyhzYW1wbGVfc3VtcyguKSA+PSA1MDAsIC4pICU+JSANCiAgICAgICAgICAgICAgICAgICBwcnVuZV90YXhhKHRheGFfc3VtcyguKSA+IDAsIC4pDQogICAgICAgICAgICAgICAgIHNhbXBsZV9kYXRhKHBzX3NwZWNpZXMpID0gc2FtcGxlX2RhdGEocHNfc3BlY2llcykgJT4lIA0KICAgICAgICAgICAgICAgICAgIGFzKCdkYXRhLmZyYW1lJykgJT4lDQogICAgICAgICAgICAgICAgICAgbXV0YXRlKFRpc3N1ZVR5cGUgPSBmY3RfcmVsZXZlbChUaXNzdWVUeXBlLCAnT3Z1bGUnKSkNCiAgICAgICAgICAgICAgICAgcmV0dXJuKHBzX3NwZWNpZXMpfSkNCnBzX3NwZWNpZXNfcmEgPSBtYXAocHNfc3BlY2llcywgfnRyYW5zZm9ybV9zYW1wbGVfY291bnRzKC54LCBmdW5jdGlvbih4KXt4IC8gc3VtKHgpfSkpDQpwc19zcGVjaWVzX2NsciA9IG1hcChwc19zcGVjaWVzLCBtaWNyb2Jpb21lOjp0cmFuc2Zvcm0sICdjbHInKQ0KcHNfc3BlY2llc19hbHIgPSBtYXAocHNfc3BlY2llcywgbWljcm9iaW9tZTo6dHJhbnNmb3JtLCAnYWxyJywgc2hpZnQ9MSkNCmBgYA0KDQpNb3N0IHByZXZhbGVudCBnZW5lcmENCmBgYHtyfQ0KcHJldl9jb3JuX2dlbnVzID0gbWFwKHBzX2dlbnVzX3JhLA0KICAgICAgICAgICAgZnVuY3Rpb24ocHNfZ2VudXNfcmEpew0KICAgICAgICAgICAgcmVsYXRpdmVfY291bnRzID0gcHNfZ2VudXNfcmEgJT4lIGdldF9jb3VudHMoKQ0KICAgICAgICAgICAgICB0YXggPSBwc19nZW51c19yYSAlPiUgZ2V0X3RheCgpDQogICAgICAgICAgICAgIHJlbGF0aXZlX2NvdW50cyAlPiUgDQogICAgICAgICAgICAgICAgcGl2b3RfbG9uZ2VyKC1mZWF0dXJlX2lkLCBuYW1lc190byA9ICdTYW1wbGVJRCcsIHZhbHVlc190byA9ICdjb3VudHMnKSAlPiUNCiAgICAgICAgICAgICAgICBsZWZ0X2pvaW4obWV0YWRhdGEsIGJ5PSdTYW1wbGVJRCcpICU+JQ0KICAgICAgICAgICAgICAgIGxlZnRfam9pbih0YXgsIGJ5PSdmZWF0dXJlX2lkJykgJT4lDQogICAgICAgICAgICAgICAgZ3JvdXBfYnkoR2VudXMpICU+JQ0KICAgICAgICAgICAgICAgIG11dGF0ZShwcmV2YWxlbmNlX2VudGlyZV9kYXRhc2V0ID0gc3VtKGNvdW50cyA+IDApIC8gbigpLA0KICAgICAgICAgICAgICAgICAgICAgICBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZV9lbnRpcmVfZGF0YXNldCA9IG1lYW4oY291bnRzKSkgJT4lDQogICAgICAgICAgICAgICAgZ3JvdXBfYnkoR2VudXMsIENvcm5fR2Vub3R5cGUpICU+JQ0KICAgICAgICAgICAgICAgIHN1bW1hcml6ZShwcmV2YWxlbmNlID0gc3VtKGNvdW50cyA+IDApIC8gbigpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZSA9IG1lYW4oY291bnRzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJldmFsZW5jZV9lbnRpcmVfZGF0YXNldCA9IGZpcnN0KHByZXZhbGVuY2VfZW50aXJlX2RhdGFzZXQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZV9lbnRpcmVfZGF0YXNldCA9IGZpcnN0KG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlX2VudGlyZV9kYXRhc2V0KSkgJT4lDQogICAgICAgICAgICAgICAgdW5ncm91cCgpfSkNCnByZXZfY29ybl9nZW51cyRgMTZTYCAlPiUgDQogIHJlbmFtZSh0b3RhbCA9IG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlX2VudGlyZV9kYXRhc2V0KSAlPiUNCiAgZGlzdGluY3QoR2VudXMsIENvcm5fR2Vub3R5cGUsIG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlLCB0b3RhbCkgJT4lDQogIG11dGF0ZShtZWFuX3JlbGF0aXZlX2FidW5kYW5jZT0gcm91bmQobWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2UsIDMpLA0KICAgICAgICAgdG90YWwgPSByb3VuZCh0b3RhbCwgMykpICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gQ29ybl9HZW5vdHlwZSwgdmFsdWVzX2Zyb20gPSBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZSkgJT4lDQogIGZpbHRlcih0b3RhbCA+PSAwLjAxKSAlPiUNCiAgYXJyYW5nZShkZXNjKHRvdGFsKSkgJT4lDQogIGtibChmb3JtYXQgPSAnaHRtbCcsIGNvbC5uYW1lcyA9IGMoJ0dlbnVzJywgJ1RvdGFsIFJlbGF0aXZlIEFidW5kYW5jZScsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdSZWxhdGl2ZSBhYnVuZGFuY2UgQjczJywgJ1JlbGF0aXZlIGFidW5kYW5jZSBDTUwzMjInKSkgJT4lDQogIGthYmxlX2NsYXNzaWMoZnVsbF93aWR0aCA9IEYsIGh0bWxfZm9udCA9ICJUaW1lcyBOZXcgUm9tYW4iKQ0KcHJldl9jb3JuX2dlbnVzJElUUyAlPiUgDQogIHJlbmFtZSh0b3RhbCA9IG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlX2VudGlyZV9kYXRhc2V0KSAlPiUNCiAgZGlzdGluY3QoR2VudXMsIENvcm5fR2Vub3R5cGUsIG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlLCB0b3RhbCkgJT4lDQogIG11dGF0ZShtZWFuX3JlbGF0aXZlX2FidW5kYW5jZT0gcm91bmQobWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2UsIDMpLA0KICAgICAgICAgdG90YWwgPSByb3VuZCh0b3RhbCwgMykpICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gQ29ybl9HZW5vdHlwZSwgdmFsdWVzX2Zyb20gPSBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZSkgJT4lDQogICNmaWx0ZXIodG90YWwgPj0gMC4wMSkgJT4lDQogIGFycmFuZ2UoZGVzYyh0b3RhbCkpICU+JQ0KICBrYmwoZm9ybWF0ID0gJ2h0bWwnLCBjb2wubmFtZXMgPSBjKCdHZW51cycsICdUb3RhbCBSZWxhdGl2ZSBBYnVuZGFuY2UnLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnUmVsYXRpdmUgYWJ1bmRhbmNlIEI3MycsICdSZWxhdGl2ZSBhYnVuZGFuY2UgQ01MMzIyJykpICU+JQ0KICBrYWJsZV9jbGFzc2ljKGZ1bGxfd2lkdGggPSBGLCBodG1sX2ZvbnQgPSAiVGltZXMgTmV3IFJvbWFuIikNCmBgYA0KTW9zdCBwcmV2YWxlbnQgc3BlY2llcw0KYGBge3J9DQpwcmV2X2Nvcm5fc3BlY2llcyA9IG1hcChwc19zcGVjaWVzX3JhLA0KICAgICAgICAgICAgZnVuY3Rpb24ocHNfc3BlY2llc19yYSl7DQogICAgICAgICAgICByZWxhdGl2ZV9jb3VudHMgPSBwc19zcGVjaWVzX3JhICU+JSBnZXRfY291bnRzKCkNCiAgICAgICAgICAgICAgdGF4ID0gcHNfc3BlY2llc19yYSAlPiUgZ2V0X3RheCgpDQogICAgICAgICAgICAgIHJlbGF0aXZlX2NvdW50cyAlPiUgDQogICAgICAgICAgICAgICAgcGl2b3RfbG9uZ2VyKC1mZWF0dXJlX2lkLCBuYW1lc190byA9ICdTYW1wbGVJRCcsIHZhbHVlc190byA9ICdjb3VudHMnKSAlPiUNCiAgICAgICAgICAgICAgICBsZWZ0X2pvaW4obWV0YWRhdGEsIGJ5PSdTYW1wbGVJRCcpICU+JQ0KICAgICAgICAgICAgICAgIGxlZnRfam9pbih0YXgsIGJ5PSdmZWF0dXJlX2lkJykgJT4lDQogICAgICAgICAgICAgICAgZ3JvdXBfYnkoU3BlY2llcykgJT4lDQogICAgICAgICAgICAgICAgbXV0YXRlKHByZXZhbGVuY2VfZW50aXJlX2RhdGFzZXQgPSBzdW0oY291bnRzID4gMCkgLyBuKCksDQogICAgICAgICAgICAgICAgICAgICAgIG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlX2VudGlyZV9kYXRhc2V0ID0gbWVhbihjb3VudHMpKSAlPiUNCiAgICAgICAgICAgICAgICBncm91cF9ieShTcGVjaWVzLCBDb3JuX0dlbm90eXBlKSAlPiUNCiAgICAgICAgICAgICAgICBzdW1tYXJpemUocHJldmFsZW5jZSA9IHN1bShjb3VudHMgPiAwKSAvIG4oKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2UgPSBtZWFuKGNvdW50cyksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHByZXZhbGVuY2VfZW50aXJlX2RhdGFzZXQgPSBmaXJzdChwcmV2YWxlbmNlX2VudGlyZV9kYXRhc2V0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2VfZW50aXJlX2RhdGFzZXQgPSBmaXJzdChtZWFuX3JlbGF0aXZlX2FidW5kYW5jZV9lbnRpcmVfZGF0YXNldCkpICU+JQ0KICAgICAgICAgICAgICAgIHVuZ3JvdXAoKX0pDQpwcmV2X2Nvcm5fc3BlY2llcyRgMTZTYCAlPiUgDQogIHJlbmFtZSh0b3RhbCA9IG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlX2VudGlyZV9kYXRhc2V0KSAlPiUNCiAgZGlzdGluY3QoU3BlY2llcywgQ29ybl9HZW5vdHlwZSwgbWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2UsIHRvdGFsKSAlPiUNCiAgbXV0YXRlKG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlPSByb3VuZChtZWFuX3JlbGF0aXZlX2FidW5kYW5jZSwgMyksDQogICAgICAgICB0b3RhbCA9IHJvdW5kKHRvdGFsLCAzKSkgJT4lDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBDb3JuX0dlbm90eXBlLCB2YWx1ZXNfZnJvbSA9IG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlKSAlPiUNCiAgZmlsdGVyKHRvdGFsID49IDAuMDEpICU+JQ0KICBhcnJhbmdlKGRlc2ModG90YWwpKSAlPiUNCiAga2JsKGZvcm1hdCA9ICdodG1sJywgY29sLm5hbWVzID0gYygnU3BlY2llcycsICdUb3RhbCBSZWxhdGl2ZSBBYnVuZGFuY2UnLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnUmVsYXRpdmUgYWJ1bmRhbmNlIEI3MycsICdSZWxhdGl2ZSBhYnVuZGFuY2UgQ01MMzIyJykpICU+JQ0KICBrYWJsZV9jbGFzc2ljKGZ1bGxfd2lkdGggPSBGLCBodG1sX2ZvbnQgPSAiVGltZXMgTmV3IFJvbWFuIikNCnByZXZfY29ybl9zcGVjaWVzJElUUyAlPiUgDQogIHJlbmFtZSh0b3RhbCA9IG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlX2VudGlyZV9kYXRhc2V0KSAlPiUNCiAgZGlzdGluY3QoU3BlY2llcywgQ29ybl9HZW5vdHlwZSwgbWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2UsIHRvdGFsKSAlPiUNCiAgbXV0YXRlKG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlPSByb3VuZChtZWFuX3JlbGF0aXZlX2FidW5kYW5jZSwgMyksDQogICAgICAgICB0b3RhbCA9IHJvdW5kKHRvdGFsLCAzKSkgJT4lDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBDb3JuX0dlbm90eXBlLCB2YWx1ZXNfZnJvbSA9IG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlKSAlPiUNCiAgZmlsdGVyKHRvdGFsID49IDAuMDEpICU+JQ0KICBhcnJhbmdlKGRlc2ModG90YWwpKSAlPiUNCiAga2JsKGZvcm1hdCA9ICdodG1sJywgY29sLm5hbWVzID0gYygnU3BlY2llcycsICdUb3RhbCBSZWxhdGl2ZSBBYnVuZGFuY2UnLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnUmVsYXRpdmUgYWJ1bmRhbmNlIEI3MycsICdSZWxhdGl2ZSBhYnVuZGFuY2UgQ01MMzIyJykpICU+JQ0KICBrYWJsZV9jbGFzc2ljKGZ1bGxfd2lkdGggPSBGLCBodG1sX2ZvbnQgPSAiVGltZXMgTmV3IFJvbWFuIikNCmBgYA0KYGBge3J9DQpyZWFkcyA9IGxpc3QoJzE2UycgPSByZWFkX3F6YShoZXJlKCdkYXRhJywgJzE2UycsICdyZXAtc2Vxcy1maWx0ZXJlZC5xemEnKSkkZGF0YSwNCiAgICAgICAgICAgICAnSVRTJyA9IHJlYWRfcXphKGhlcmUoJ2RhdGEnLCAnSVRTJywgJ3JlcC1zZXFzLWZpbHRlcmVkLnF6YScpKSRkYXRhKQ0KYGBgDQoNCkJlbG93IGFyZSBiYXJwbG90cyBvZiByZWxhdGl2ZSB0YXhvbiBhYnVuZGFuY2VzIGZvciAxNlMgc2VxdWVuY2luZyB3aXRoIHNhbXBsZXMgZ3JvdXBlZCBhY2NvcmRpbmcgdG8gc2ltaWxhcml0eSB1c2luZyB0aGUgbmVhdG1hcCBtZXRob2QuIA0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEwLCB3YXJuaW5nPUZBTFNFLCBmaWcuc2hvdz0naGlkZSd9DQojIyBodHRwczovL2dpdGh1Yi5jb20vZ29vZ2xlL3BhbGV0dGUuanMvYmxvYi83OWE3MDNkZjM0NGUzYjI0MzgwY2UxYTIxMWEyZGY3ZjJkOTBjYTIyL3BhbGV0dGUuanMjTDgwMg0KbXBuNjUgPSBjKCcjZmYwMDI5JywnIzM3N2ViOCcsJyM2NmE2MWUnLCcjOTg0ZWEzJywnIzAwZDJkNScsJyNmZjdmMDAnLCcjYWY4ZDAwJywnIzdmODBjZCcsJyNiM2U5MDAnLCcjYzQyZTYwJywnI2E2NTYyOCcsDQogICAgICAgICAnI2Y3ODFiZicsJyM4ZGQzYzcnLCcjYmViYWRhJywnI2ZiODA3MicsJyM4MGIxZDMnLCcjZmRiNDYyJywnI2ZjY2RlNScsJyNiYzgwYmQnLCcjZmZlZDZmJywnI2M0ZWFmZicsJyNjZjhjMDAnLA0KICAgICAgICAgJyMxYjllNzcnLCcjZDk1ZjAyJywnI2U3Mjk4YScsJyNlNmFiMDInLCcjYTY3NjFkJywnIzAwOTdmZicsJyMwMGQwNjcnLCcjMDAwMDAwJywnIzI1MjUyNScsJyM1MjUyNTInLCcjNzM3MzczJywNCiAgICAgICAgICcjOTY5Njk2JywnI2JkYmRiZCcsJyNmNDM2MDAnLCcjNGJhOTNiJywnIzU3NzliYicsJyM5MjdhY2MnLCcjOTdlZTNmJywnI2JmMzk0NycsJyM5ZjViMDAnLCcjZjQ4NzU4JywnIzhjYWVkNicsDQogICAgICAgICAnI2YyYjk0ZicsJyNlZmYyNmUnLCcjZTQzODcyJywnI2Q5YjEwMCcsJyM5ZDdhMDAnLCcjNjk4Y2ZmJywnI2Q5ZDlkOScsJyMwMGQyN2UnLCcjZDA2ODAwJywnIzAwOWY4MicsJyNjNDkyMDAnLA0KICAgICAgICAgJyNjYmU4ZmYnLCcjZmVjZGRmJywnI2MyN2ViNicsJyM4Y2QyY2UnLCcjYzRiOGQ5JywnI2Y4ODNiMCcsJyNhNDkxMDAnLCcjZjQ4ODAwJywnIzI3ZDBkZicsJyNhMDRhOWInKQ0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9OH0NCm1hcChyYW5rX25hbWVzKHBzX2dlbnVzJGAxNlNgKVsyOjZdLCBmdW5jdGlvbih0YXhfcmFuayl7DQogIGRmID0gcHNfZ2VudXMkYDE2U2AgJT4lIA0KICAgIHNwZWVkeXNlcTo6bXV0YXRlX3NhbXBsZV9kYXRhKGNvbmRpdGlvbiA9IGNvbmRpdGlvbl93X3JlcCkgJT4lDQogICAgdHJhbnNmb3JtKHRyYW5zZm9ybSA9ICJjb21wb3NpdGlvbmFsIikgJT4lDQogICAgYWdncmVnYXRlX3JhcmUobGV2ZWwgPSB0YXhfcmFuaywgZGV0ZWN0aW9uID0gMC4wNSwgcHJldmFsZW5jZSA9IDAuMDUpIA0KICBwID0gcGxvdF9jb21wb3NpdGlvbihkZiwgeC5sYWJlbD0nY29uZGl0aW9uJywgb3R1LnNvcnQgPSAnYWJ1bmRhbmNlJywgc2FtcGxlLnNvcnQ9J25lYXRtYXAnKSArDQogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAxKSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9bXBuNjUpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT05MCxoanVzdD0wLCB2anVzdD0wLjUpKSArDQogIGxhYnMoeCA9ICJTYW1wbGUgY29uZGl0aW9uIiwNCiAgICAgICB5ID0gIlJlbGF0aXZlIGFidW5kYW5jZSIsDQogICAgICAgdGl0bGUgPSBnbHVlKCIxNlMgUmVsYXRpdmUgYWJ1bmRhbmNlIGF0IHt0YXhfcmFua30gbGV2ZWwiKSwgDQogICAgICAgZmlsbCA9IHRheF9yYW5rKQ0KICAgICBwICU+JSBwbG90bHk6OmdncGxvdGx5KCkNCn0pDQpgYGANCg0KDQo8YnI+PGJyPjxicj48YnI+PGJyPjxicj4gVGhlIG5leHQgdHdvIHNldHMgYXJlIGRvbmUgd2l0aCBJVFMgY291bnRzIGJ1dCBtYWRlIHRoZSBzYW1lIHdheSBhcyBhYm92ZS4NCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD04fQ0KbWFwKHJhbmtfbmFtZXMocHNfZ2VudXMkSVRTKVsyOjZdLCBmdW5jdGlvbih0YXhfcmFuayl7DQogIGRmID0gcHNfZ2VudXMkSVRTICU+JSANCiAgICBzcGVlZHlzZXE6Om11dGF0ZV9zYW1wbGVfZGF0YShjb25kaXRpb24gPSBjb25kaXRpb25fd19yZXApICU+JQ0KICAgIHRyYW5zZm9ybSh0cmFuc2Zvcm0gPSAiY29tcG9zaXRpb25hbCIpICU+JQ0KICAgIGFnZ3JlZ2F0ZV9yYXJlKGxldmVsID0gdGF4X3JhbmssIGRldGVjdGlvbiA9IDAuMDUsIHByZXZhbGVuY2UgPSAwLjA1KSANCiAgcCA9IHBsb3RfY29tcG9zaXRpb24oZGYsIHgubGFiZWw9J2NvbmRpdGlvbicsIG90dS5zb3J0ID0gJ2FidW5kYW5jZScsIHNhbXBsZS5zb3J0PSduZWF0bWFwJykgKw0KICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChuY29sID0gMSkpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPW1wbjY1KSArDQogIHRoZW1lX21pbmltYWwoKSArIA0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9OTAsaGp1c3Q9MCwgdmp1c3Q9MC41KSkgKw0KICBsYWJzKHggPSAiU2FtcGxlIGNvbmRpdGlvbiIsDQogICAgICAgeSA9ICJSZWxhdGl2ZSBhYnVuZGFuY2UiLA0KICAgICAgIHRpdGxlID0gZ2x1ZSgiSVRTIFJlbGF0aXZlIGFidW5kYW5jZSBhdCB7dGF4X3Jhbmt9IGxldmVsIiksIA0KICAgICAgIGZpbGwgPSB0YXhfcmFuaykNCiAgcCAlPiUgcGxvdGx5OjpnZ3Bsb3RseSgpDQp9KQ0KYGBgDQoNCg0KYGBge3IsIGZpZy53aWR0aD0xNSwgZmlnLmhlaWdodD04LCB3YXJuaW5nPUZBTFNFfQ0KcHJ1bmVfdGF4YShuYW1lcyhzb3J0KHRheGFfc3Vtcyhwc19nZW51c19yYSRgMTZTYCksZGVjcmVhc2luZyA9IFRSVUUpWzE6MzBdKSwgcHNfZ2VudXNfcmEkYDE2U2ApICU+JQ0KICBzcGVlZHlzZXE6OnBsb3RfaGVhdG1hcChtZXRob2QgPSAiTk1EUyIsIGRpc3RhbmNlID0gImJyYXkiLCBzYW1wbGUubGFiZWwgPSAnY29uZGl0aW9uJywgdGF4YS5sYWJlbD0nR2VudXMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT05MCxoanVzdD0wLCB2anVzdD0wLjUsIHNpemUgPSAxMCksDQogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjIpKSArDQogIGxhYnModGl0bGUgPSAnMTZTIEhlYXRtYXAgb2YgdG9wIDMwIG1vc3QgYWJ1bmRhbnQgZ2VuZXJhJykNCmBgYA0KDQo8YnI+PGJyPjxicj48YnI+PGJyPjxicj4NCg0KYGBge3IsIGZpZy53aWR0aD0xNSwgZmlnLmhlaWdodD04LCB3YXJuaW5nPUZBTFNFfQ0KcHJ1bmVfdGF4YShuYW1lcyhzb3J0KHRheGFfc3Vtcyhwc19nZW51c19yYSRJVFMpLGRlY3JlYXNpbmcgPSBUUlVFKVsxOjMwXSksIHBzX2dlbnVzX3JhJElUUykgJT4lDQogIHNwZWVkeXNlcTo6cGxvdF9oZWF0bWFwKG1ldGhvZCA9ICJOTURTIiwgZGlzdGFuY2UgPSAiYnJheSIsIHNhbXBsZS5sYWJlbCA9ICdjb25kaXRpb24nLCB0YXhhLmxhYmVsPSdHZW51cycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTkwLGhqdXN0PTAsIHZqdXN0PTAuNSwgc2l6ZSA9IDEwKSwNCiAgICAgICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiksIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0yMikpICsNCiAgbGFicyh0aXRsZSA9ICdJVFMgSGVhdG1hcCBvZiB0b3AgMzAgbW9zdCBhYnVuZGFudCBnZW5lcmEnKQ0KYGBgDQpgYGB7cn0NCnNhdmUuaW1hZ2UoaGVyZSgnc3JjLzE2X2FuZF9JVFNfaW1wb3J0LlJEYXRhJykpDQpgYGANCg0K